package cn.coder.toolkit;

import lombok.Getter;

import java.util.HashMap;
import java.util.Map;

/**
 * 转移规则可以如此理解: 在 SAVED 状态时允许执行 SAVE 和 DELETE 动作, 执行完成后状态机的状态变为 SAVED
 *
 * <pre>
 *     // 定义构建器, 便于在需要时构建状态机
 *     // 转移规则有两层含义, 1. 是否允许在某个状态执行某个操作, 2. 执行操作成功后状态机内部状态发生对应改变
 *     private static final StateMachine.Builder<ProcessStateEnum, ProcessEventEnum> BUILDER;
 *     static {
 *         BUILDER = new StateMachine.Builder<ProcessStateEnum, ProcessEventEnum>()
 *             // SAVED
 *             .from(ProcessStateEnum.SAVED).to(ProcessStateEnum.SAVED).on(ProcessEventEnum.SAVE, ProcessEventEnum.DELETE)// 允许保存多次/允许删除
 *             .from(ProcessStateEnum.SAVED).to(ProcessStateEnum.SUBMITTED).on(ProcessEventEnum.SUBMIT) // 提交审批
 *             // SUBMITTED
 *             .from(ProcessStateEnum.SUBMITTED).to(ProcessStateEnum.APPROVED).on(ProcessEventEnum.APPROVE) // 审批通过
 *             .from(ProcessStateEnum.SUBMITTED).to(ProcessStateEnum.REJECTED).on(ProcessEventEnum.REJECT) // 审批驳回
 *             .from(ProcessStateEnum.SUBMITTED).to(ProcessStateEnum.SAVED).on(ProcessEventEnum.CANCEL) // 撤销提交
 *             // REJECTED
 *             .from(ProcessStateEnum.REJECTED).to(ProcessStateEnum.SAVED).on(ProcessEventEnum.SAVE, ProcessEventEnum.DELETE) // 驳回后可重新保存/也可删除
 *             // APPROVED, 终态
 *             // Enter 回调
 *             .onEnter(ProcessStateEnum.SAVED, e -> System.out.println("进入 SAVED 状态，事件=" + e))
 *             .onEnter(ProcessStateEnum.SUBMITTED, e -> System.out.println("进入 SUBMITTED 状态，事件=" + e))
 *             .onEnter(ProcessStateEnum.APPROVED, e -> System.out.println("进入 APPROVED 状态，事件=" + e))
 *             .onEnter(ProcessStateEnum.REJECTED, e -> System.out.println("进入 REJECTED 状态，事件=" + e))
 *         ;
 *     }
 * </pre>
 *
 * <pre>
 *     // 构建状态机并通过 fire 验证当前状态是否可执行对应操作, 验证通过后通过 machine.getCurrentState().name() 获取当前状态机的状态
 *     Process select = mapper.selectById(request.getId());
 *     if (null == select) {
 *         throw new BusinessException("数据不存在");
 *     }
 *     StateMachine<ProcessStateEnum, ProcessEventEnum> machine = BUILDER.build(ProcessStateEnum.valueOf(select.getState()));
 *     if (!machine.fire(ProcessEventEnum.SAVE)) {
 *         throw new BusinessException("状态不正确");
 *     }
 *     Process update = new Process().setId(select.getId()).setState(machine.getCurrentState().name()).setName(request.getName());
 *     return mapper.updateById(update);
 * </pre>
 */
@Getter
public class StateMachine<S, E> {

    @FunctionalInterface
    public interface StateChangeCallback<S, E> {
        void accept(S from, S to, E event);
    }

    private final Map<S, Map<E, S>> transitions; // 转移规则, 有两层含义, 1. 是否允许在某个状态执行某个操作, 2. 执行操作成功后状态机内部状态发生对应改变
    private final Map<S, StateChangeCallback<S, E>> onEnterCallbacks; // 进入回调, from:旧状态, to:新状态, event:触发事件
    private S currentState;

    private StateMachine(S initState,
                         Map<S, Map<E, S>> transitions,
                         Map<S, StateChangeCallback<S, E>> onEnterCallbacks) {
        this.currentState = initState;
        this.transitions = transitions;
        this.onEnterCallbacks = onEnterCallbacks;
    }

    /** 触发事件，返回是否转移成功 */
    public boolean fire(E event) {
        Map<E, S> stateMap = transitions.get(currentState);
        if (stateMap != null && stateMap.containsKey(event)) {
            S prevState = currentState;
            S nextState = stateMap.get(event);
            currentState = nextState;
            if (onEnterCallbacks.containsKey(nextState)) {
                onEnterCallbacks.get(nextState).accept(prevState, nextState, event);
            }
            return true;
        }
        return false;
    }

    // -------- Builder DSL ----------
    public static class Builder<S, E> {
        private final Map<S, Map<E, S>> transitions = new HashMap<>();
        private final Map<S, StateChangeCallback<S, E>> onEnterCallbacks = new HashMap<>();

        public FromBuilder from(S fromState) {
            return new FromBuilder(fromState);
        }

        public Builder<S, E> onEnter(S state, StateChangeCallback<S, E> callback) {
            onEnterCallbacks.put(state, callback);
            return this;
        }

        public StateMachine<S, E> build(S initState) {
            return new StateMachine<>(initState, transitions, onEnterCallbacks);
        }

        // --- 内部 DSL builder ---
        public class FromBuilder {
            private final S from;

            private FromBuilder(S from) {
                this.from = from;
            }

            public ToBuilder to(S to) {
                return new ToBuilder(from, to);
            }
        }

        public class ToBuilder {
            private final S from;
            private final S to;

            private ToBuilder(S from, S to) {
                this.from = from;
                this.to = to;
            }

            @SafeVarargs
            public final Builder<S, E> on(E... events) {
                Map<E, S> map = transitions.computeIfAbsent(from, k -> new HashMap<>());
                for (E e : events) {
                    map.put(e, to);
                }
                return Builder.this;
            }
        }
    }
}
